﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Logger;
using Cofe.Core.Script;
using Cofe.Web.Interfaces;
using log4net;

namespace Cofe.Web.Feed
{
    #region QueryingEvents, RequeryingEvents
    public class QueryingEvents<T> : ScriptCommandBase
        where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        public QueryingEvents()
            : base("QueryingEvents", "EventBuffer")
        {
            Log.Debug("Started feed writing.");
        }

        public override bool CanExecute(ScriptParameterDic pm)
        {
            return base.CanExecute(pm) && pm.ContainsKey("EventBuffer") &&
                pm["EventBuffer"] is EventBuffer<T>;
        }

        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            var eventBuffer = pm["EventBuffer"] as EventBuffer<T>;
            Log.DebugFormat("Querying events. Number of events found: [{0}].", eventBuffer.Size);

            if (eventBuffer.IsEmpty)
                return ResultCommand.OK;
            else
            {
                return new IdentifyingRecentEventsFeed<T>(eventBuffer.Take(CofeWsServices.CofeWsSettings.FeedBatchSize));
            }
        }

    }

    public class RequeryingEvents<T> : ScriptCommandBase
      where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private Feed<T> _recentEventsFeed;

        public RequeryingEvents(Feed<T> recentEventsFeed)
            : base("RequeryingEvents")
        {
            _recentEventsFeed = recentEventsFeed;
        }

        public override bool CanExecute(ScriptParameterDic pm)
        {
            return base.CanExecute(pm) && pm.ContainsKey("EventBuffer") &&
                pm["EventBuffer"] is EventBuffer<T>;
        }

        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            var eventBuffer = pm["EventBuffer"] as EventBuffer<T>;
            IEnumerable<IEventLog<T>> events = eventBuffer.Take(CofeWsServices.CofeWsSettings.FeedBatchSize);

            Log.DebugFormat("Requerying events. Number of events found: [{0}].", events.Count());

            if (events.Count().Equals(0))
            {
                return new SavingRecentEventsFeed<T>(_recentEventsFeed);
            }

            return new UpdatingRecentEventsFeed<T>(_recentEventsFeed, events);
        }
    }
    #endregion

    #region IdentifyingRecentEventsFeed, CreatingNewRecentEventsFeed
    public class IdentifyingRecentEventsFeed<T> : ScriptCommandBase
        where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private IEnumerable<IEventLog<T>> _events;

        public IdentifyingRecentEventsFeed(IEnumerable<IEventLog<T>> events)
            : base("IdentifyingRecentEventsFeed")
        {
            _events = events;
            Log.Debug("Identifying recent events feed.");
        }

        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            var feedService = CofeWsServices.Feeds.GetFeedService<T>();
            string fileName = feedService .FileSystem.GetLatestFeed();
            if (fileName != null)
            {
                Feed<T> recentEventsFeed = feedService.Builder.LoadRecentEventsFeed(fileName);
                Log.DebugFormat("Found recent events feed. {0}", recentEventsFeed);
                pm.AddOrUpdate("CurrentFeed", recentEventsFeed);
                return new UpdatingRecentEventsFeed<T>(recentEventsFeed, _events);
            }
            Log.Debug("No recent events feed found.");
            return new CreatingNewRecentEventsFeed<T>(_events);
        }
    }

    public class CreatingNewRecentEventsFeed<T> : ScriptCommandBase
     where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private IEnumerable<IEventLog<T>> _events;

        public CreatingNewRecentEventsFeed(IEnumerable<IEventLog<T>> events)
            : base("CreatingNewRecentEventsFeed")
        {
            _events = events;
            Log.Debug("Creating new recent events feed.");
        }

        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            Feed<T> feed = CofeWsServices.Feeds.GetFeedService<T>().Builder
                .CreateRecentEventsFeed(FeedId.Initial, null);
            return new UpdatingRecentEventsFeed<T>(feed, _events);
        }

    }
    #endregion

    #region UpdatingRecentEventsFeed, ArchivingRecentEventsFeed, SavingRecentEventsFeed

    public class UpdatingRecentEventsFeed<T> : ScriptCommandBase
        where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private IEnumerable<IEventLog<T>> _events;
        private Feed<T> _recentEventsFeed;

        public UpdatingRecentEventsFeed(Feed<T> recentEventsFeed, IEnumerable<IEventLog<T>> events)
            : base("UpdatingRecentEventsFeed")
        {
            _events = events;
            _recentEventsFeed = recentEventsFeed;
            Log.DebugFormat("Updating recent events feed. {0}", recentEventsFeed);
        }

        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            Queue<IEventLog<T>> remainingEvents = new Queue<IEventLog<T>>();

            foreach (var evnt in _events)
            {
                if (_recentEventsFeed.IsFull())
                    remainingEvents.Enqueue(evnt);
                else
                    _recentEventsFeed.AddEvent(evnt);
            }

            if (remainingEvents.Count > 0)
            {
                return new ArchivingRecentEventsFeed<T>(_recentEventsFeed, remainingEvents);
            }

            return new RequeryingEvents<T>(_recentEventsFeed);
        }
    }

    public class ArchivingRecentEventsFeed<T> : ScriptCommandBase
        where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private IEnumerable<IEventLog<T>> _events;
        private Feed<T> _recentEventsFeed;

        public ArchivingRecentEventsFeed(Feed<T> recentEventsFeed, IEnumerable<IEventLog<T>> events)
            : base("ArchivingRecentEventsFeed")
        {
            _events = events;
            _recentEventsFeed = recentEventsFeed;

        }

        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            var feedService = CofeWsServices.Feeds.GetFeedService<T>();
            Feed<T> nextFeed = feedService.Builder.CreateNextFeed(_recentEventsFeed.FeedId);
            Feed<T> archiveFeed = feedService.Builder.CreateArchiveFeed(_recentEventsFeed, 
                _recentEventsFeed.FeedId, nextFeed.FeedId);

            Log.DebugFormat("Archiving recent events feed. {0}", archiveFeed);

            archiveFeed.Save();

            return new UpdatingRecentEventsFeed<T>(nextFeed, _events);
        }
    }

    public class SavingRecentEventsFeed<T> : ScriptCommandBase
      where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private Feed<T> _recentEventsFeed;

        public SavingRecentEventsFeed(Feed<T> recentEventsFeed)
            : base("SavingRecentEventsFeed")
        {
            _recentEventsFeed = recentEventsFeed;
            Log.DebugFormat("Saving recent events feed. {0}", recentEventsFeed);
        }


        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            _recentEventsFeed.Save();
            return new NotifyingListeners<T>(_recentEventsFeed);
        }
    }
    #endregion

    #region NotifyingListeners

    public class NotifyingListeners<T> : ScriptCommandBase
     where T : EventArgs
    {
        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private Feed<T> _recentEventsFeed;

        public NotifyingListeners(Feed<T> recentEventsFeed)
            : base("NotifyingListeners")
        {
            _recentEventsFeed = recentEventsFeed;
            Log.Debug("Notifying listeners.");
        }


        public override IScriptCommand Execute(ScriptParameterDic pm)
        {
            CofeWsServices.Feeds.GetFeedService<T>().NotifyRecentFeedChanged(this,
                new FeedChangedEventArgs(_recentEventsFeed));
            
            return ResultCommand.OK;
        }
    }

    #endregion


}
